//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#ifndef __MACLAYER_SMACLAYER_H_
#define __MACLAYER_SMACLAYER_H_

#define TIMERS	5

#include <omnetpp.h>
#include <BaseMacLayer.h>
#include <SMacMsg_m.h>
#include <ChannelSenseRequest_m.h>

typedef enum {SCHED_STATE_SLEEP, SCHED_STATE_OWN, SCHED_STATE_OTHER, SCHED_STATE_STARTUP} SchedStates;

typedef enum {PROTO_STATE_INVALID,PROTO_STATE_IDLE,PROTO_STATE_CONTEND, PROTO_STATE_WFCTS,PROTO_STATE_WFDATA,PROTO_STATE_SEND_RTS, \
			  PROTO_STATE_SEND_CTS, PROTO_STATE_SEND_DATA,PROTO_STATE_SEND_SYNC, PROTO_STATE_WFACK, PROTO_STATE_SEND_ACK} ProtoState;

class SMACLayer : public BaseMacLayer
{
public:
	enum NAVStates{
		NAV_CLEAR,
		NAV_BUSY
		};
	enum SmacMessageKinds {
		  //between MAC layers of two nodes
		  RTS = LAST_BASE_MAC_MESSAGE_KIND, // request to send
		  CTS,                 // clear to send
		  ACK,                 // acknowledgement
		  DATA,
		  SYN,
		  BROADCAST,
		  LAST_MAC_Smac_MESSAGE_KIND
		};
	enum SmacTimerKinds {
		TIMER_PROTOCOL,
		TIMER_NAV,
		TIMER_SCHED	,
		TIMER_ACTIVE,
		TIMEOUT
	};
protected: //parameters
	//parameters from ini file
		simtime_t listen_time;
		simtime_t sleep_time;
		simtime_t frame_time;
		simtime_t allowDrift;
		double bitrate;
		double txPower;
		double centerFreq;

private : //scalars need to be records
		simtime_t time_sendmsg;
		long nb_net_package;
		long nb_contend_fail;
		long nb_buffer_full_drop;
		long nb_received_nav;
		cLongHistogram delayStats;

private : //some flags
		bool is_transmitting;
		int must_send_sync;

protected:
		typedef std::list<SMacMsg*> SMacMsgList;
		SMacMsgList  tx_msgQuene;
		double clock_skew;	 // make sure every node not all synched
		simtime_t sched_table[10]; // neighbour schedules as delta of own
		int sched_count;
		SchedStates sched_state;
		simtime_t time_last_sched;	// the last time at which my own sched was
		ProtoState proto_state;
		ProtoState proto_next_state;
		int nav_state;			// for NAV
		simtime_t nav_end_time;
		unsigned int queueLength ;  // buffer length
		int cts_to;
		simtime_t cts_nav_end;
		simtime_t cts_nav_rcv;
		simtime_t cts_nav_t;
		int ack_to;
		int ack_serial;
		int my_serial;
		int fsc;
		// @brief Time to switch radio from Rx to Tx state
		simtime_t aTurnaroundTime;
		// TIMERS:
		cMessage* timeout_msg[TIMERS];
		/** @brief Used to sense if the channel is idle for contention periods*/
		ChannelSenseRequest* contention;
		/** @brief Timer used to indicate the end of a SIFS*/
		ChannelSenseRequest* endSifs;
		/** @brief Stores the the time a channel sensing started.
		 * Used to calculate the quiet-time of the channel if the sensing was
		 * aborted. */
		simtime_t chSenseStart;
protected:
		virtual void initialize(int);
		virtual void handleSelfMsg(cMessage* msg);
		virtual void handleUpperMsg(cMessage* msg);
		virtual void handleLowerMsg(cMessage* msg);
		virtual void handleLowerControl(cMessage* msg);
		virtual void finish();
		virtual ~SMACLayer();
protected: //functions
		void switchRadio(Radio::RadioState state);
		void setMySchedule(simtime_t time); // set my sched to start <x> ago
		void evalState();	// check state, may start transmission
		void startContending(int time);	// contend, ctime = <x>
		void sendSync();	// send SYNC _now_
		void sendRts();
		void sendCts();
		void sendData();
		void sendAck();
		void receiveSync(cMessage* msg);	// received SYNC
		void receiveRts(cMessage* msg);
		void receiveCts(cMessage* msg);
		void receiveData(cMessage* msg);
		void receiveAck(cMessage* msg);
		void adoptSchedule(int offset); // extra schedule
		void calcSchedState();	// recalculate: sleep or listen
		void setIdle();
		void protocolTimeout();
		void navTimeout();
		void schedTimeout();
		void setTimeout(simtime_t ticks, int which);
		void cancelTimeout(int which);
		void setProtocolTimeout(simtime_t t);
		void setNavTimeout(simtime_t t);
		void setSchedTimeout(simtime_t t);
		simtime_t WaitTimeOut(SmacMessageKinds type);
		void updateNav(simtime_t nav);
		void txDone();
		void adoptSchedule(simtime_t offset);
		int isSameSchedule(simtime_t time1, simtime_t time2);
		virtual void transmitDone();
		void attachSignal(SMacMsg* macPkt,simtime_t startTime);
		/** @brief encapsulate packet */
		SMacMsg* encapsMsg(cPacket *netw);
		/** @brief decapsulate packet */
		cMessage* decapsMsg(SMacMsg *frame);
		simtime_t packetDuration(double bits, double br);
		Signal* createSignal(simtime_t start, simtime_t length, double power, double bitrate);

protected: //class utility
		std::string getStateString(Radio::RadioState state);

};

#endif
